
#pragma once

#include "types.h"
#include "utilities.h"

namespace tools
{

    namespace wrapper_details{

        template<class T>struct IHelper
        {
            template<class G> bool IsSame()const
            {
                typedef typename ::std::conditional<PropertyType<G>::eIS_STRICT , char, int>::type Strategy;
                return IsSame<G>( Strategy() );
            }

            virtual void Deleter(const Byte* src, const bool alloc)const=0;
            virtual T*   CloneObjectIntoStack(const Byte* data, Byte* dst)const=0;
            virtual T*   CloneObjectIntoHeap(const Byte* data)const=0;
            virtual void CloneMeIntoStack(Byte* dst)const=0;

            virtual bool IsTypeSame(const ::std::type_info& info)const=0;
            virtual bool IsTypeSame(const eTYPE& type)const=0;

            virtual size_t GetSize()const=0;
            virtual bool IsRegistred()const=0;

            virtual bool IsFloating()const=0;
            virtual bool IsInteger()const=0;

            virtual const ::std::type_info& GetTypeInfo()const=0;
            virtual const char* GetTypeName()const=0;
            virtual eTYPE GetType()const=0;
        private:
            template<class G> bool IsSame(int)const  { return IsTypeSame( static_cast<eTYPE>(Type2Enum<G>::eTYPE) ); }
            template<class G> bool IsSame(char)const { return IsTypeSame( typeid(G) ); }
        };
    //-----------------------------------------------------------------------------------------

        template<class U,class T> struct Helper: IHelper<T>
        {
            static_assert(std::is_convertible<U*,T*>::value || ::std::is_same<T,void>::value, "TYPES ARE NOT CONVERTED");

            const U* Cast(const Byte* data)const { return reinterpret_cast<const U*>(data); }

            #ifndef _MSC_VER
                //механизм умеет корректно вызывать правильный диструктор даже если в базовом классе он не виртуален
                //поэтому, предупреждение от компилятора отключено
                #pragma GCC diagnostic push
                #pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
                 void Deleter(const Byte* data, const bool alloc)const override { (alloc) ? delete Cast(data) : Cast(data)->~U(); }
                #pragma GCC diagnostic pop
            #else
                 void Deleter(const Byte* data, const bool alloc)const override { (alloc) ? delete Cast(data) : Cast(data)->~U(); }
            #endif

            T*     CloneObjectIntoStack(const Byte* data, Byte* dst)const override { return new(dst) U( *Cast(data) ); }
            T*     CloneObjectIntoHeap(const Byte* data)const override            { return new U( *Cast(data) );      }
            void   CloneMeIntoStack(Byte* dst)const override                      { new(dst) Helper;                  }
            bool   IsTypeSame(const ::std::type_info& info)const override         { return info == typeid(U);         }
            bool   IsTypeSame(const eTYPE& type)const override                    { return static_cast<eTYPE>(Type2Enum<U>::eTYPE)==type; }
            size_t GetSize()const override                                        { return sizeof(U);                 }
            bool   IsRegistred()const override { return static_cast<eTYPE>(Type2Enum<U>::eTYPE)!=eNOT_REGISTRED; }
            
            bool IsFloating()const override
            {
                const eTYPE type = static_cast<eTYPE>(Type2Enum<U>::eTYPE);
                return type == eF32 || type == eF64;
            }
            bool IsInteger()const override
            {
                const eTYPE type = static_cast<eTYPE>(Type2Enum<U>::eTYPE);
                return type == eI8 || type == eI16 || type == eI32 || type == eI64 || 
                    type == eU8 || type == eU16 || type == eU32 || type == eU64 || type==eBYTE;
            }
            const ::std::type_info& GetTypeInfo()const override { return typeid(U);              }
            const char* GetTypeName()const override             { return Type2String<U>::Name(); } 
            eTYPE GetType()const override { return static_cast<eTYPE>(Type2Enum<U>::eTYPE);       } 
        };

        //-----------------------------------------------------------------------------------------

    }//namespace wrapper_details



}//namespace tools

